]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
[MessageTray] Clean up source-vs-notification icon handling
authorDan Winship <danw@gnome.org>
Thu, 5 Aug 2010 17:09:27 +0000 (13:09 -0400)
committerDan Winship <danw@gnome.org>
Thu, 26 Aug 2010 15:00:54 +0000 (11:00 -0400)
A Source needs exactly one summary icon (which in the case of a
trayicon-based source won't even be just an image), but possibly many
notification icons, which may vary for successive notifications
(particularly in the case of NotificationDaemon notifications). So
differentiate these cases in the API.

https://bugzilla.gnome.org/show_bug.cgi?id=627303

js/ui/messageTray.js
js/ui/notificationDaemon.js
js/ui/telepathyClient.js
js/ui/windowAttentionHandler.js

index 1391aac3979d1c04a0cd9eecea4d6c64816b2123..1138a0bfecd39b54ca28e2101935111af304a5cc 100644 (file)
@@ -19,7 +19,6 @@ const SUMMARY_TIMEOUT = 1;
 
 const HIDE_TIMEOUT = 0.2;
 
-const ICON_SIZE = 24;
 const BUTTON_ICON_SIZE = 36;
 
 const MAX_SOURCE_TITLE_WIDTH = 180;
@@ -46,10 +45,14 @@ function _cleanMarkup(text) {
 // @banner: the banner text
 // @params: optional additional params
 //
-// Creates a notification. In banner mode, it will show
-// @source's icon, @title (in bold) and @banner, all on a single line
+// Creates a notification. In banner mode, it will show an
+// icon, @title (in bold) and @banner, all on a single line
 // (with @banner ellipsized if necessary).
 //
+// By default, the icon shown is created by calling
+// source.createNotificationIcon(). However, you can override this
+// by passing an 'icon' parameter in @params.
+//
 // Additional notification details can be added, in which case the
 // notification can be expanded by moving the pointer into it. In
 // expanded mode, the banner text disappears, and there can be one or
@@ -150,6 +153,7 @@ Notification.prototype = {
     update: function(title, banner, params) {
         params = Params.parse(params, { bannerBody: this._bannerBody,
                                         body: null,
+                                        icon: null,
                                         clear: false });
 
         if (this._icon)
@@ -167,7 +171,7 @@ Notification.prototype = {
 
         this._bannerBody = params.bannerBody;
 
-        this._icon = this.source.createIcon(ICON_SIZE);
+        this._icon = params.icon || this.source.createNotificationIcon();
         this.actor.add(this._icon, { row: 0,
                                      col: 0,
                                      x_expand: false,
@@ -560,21 +564,30 @@ Notification.prototype = {
 };
 Signals.addSignalMethods(Notification.prototype);
 
-function Source(title, createIcon) {
-    this._init(title, createIcon);
+function Source(title) {
+    this._init(title);
 }
 
 Source.prototype = {
-    _init: function(title, createIcon) {
+    ICON_SIZE: 24,
+
+    _init: function(title) {
         this.title = title;
-        if (createIcon)
-            this.createIcon = createIcon;
+        this._iconBin = new St.Bin({ width: this.ICON_SIZE,
+                                     height: this.ICON_SIZE });
+    },
+
+    // Called to create a new icon actor (of size this.ICON_SIZE).
+    // Must be overridden by the subclass if you do not pass icons
+    // explicitly to the Notification() constructor.
+    createNotificationIcon: function() {
+        throw new Error('no implementation of createNotificationIcon in ' + this);
     },
 
-    // This can be overridden by a subclass, or by the createIcon
-    // parameter to _init()
-    createIcon: function(size) {
-        throw new Error('no implementation of createIcon in ' + this);
+    // Unlike createNotificationIcon, this always returns the same actor;
+    // there is only one summary icon actor for a Source.
+    getSummaryIcon: function() {
+        return this._iconBin;
     },
 
     notify: function(notification) {
@@ -600,6 +613,15 @@ Source.prototype = {
 
     destroy: function() {
         this.emit('destroy');
+    },
+
+    //// Protected methods ////
+
+    // The subclass must call this at least once to set the summary icon.
+    _setSummaryIcon: function(icon) {
+        if (this._iconBin.child)
+            this._iconBin.child.destroy();
+        this._iconBin.child = icon;
     }
 };
 Signals.addSignalMethods(Source.prototype);
@@ -626,7 +648,7 @@ SummaryItem.prototype = {
         this._sourceBox.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
         this._sourceBox.connect('allocate', Lang.bind(this, this._allocate));
 
-        this._sourceIcon = source.createIcon(ICON_SIZE);
+        this._sourceIcon = source.getSummaryIcon();
         this._sourceTitleBin = new St.Bin({ y_align: St.Align.MIDDLE, x_fill: true });
         this._sourceTitle = new St.Label({ style_class: 'source-title',
                                            text: source.title });
index f4a3441db71807ddee9ba3241273741c1267ba81..3081f8709fcc2e77bad09af5a31a6860bef64a4e 100644 (file)
@@ -141,6 +141,37 @@ NotificationDaemon.prototype = {
         }
     },
 
+    _iconForNotificationData: function(icon, hints, size) {
+        let textureCache = St.TextureCache.get_default();
+
+        if (icon) {
+            if (icon.substr(0, 7) == 'file://')
+                return textureCache.load_uri_async(icon, size, size);
+            else if (icon[0] == '/') {
+                let uri = GLib.filename_to_uri(icon, null);
+                return textureCache.load_uri_async(uri, size, size);
+            } else
+                return textureCache.load_icon_name(icon, size);
+        } else if (hints.icon_data) {
+            let [width, height, rowStride, hasAlpha,
+                 bitsPerSample, nChannels, data] = hints.icon_data;
+            return textureCache.load_from_raw(data, data.length, hasAlpha,
+                                              width, height, rowStride, size);
+        } else {
+            let stockIcon;
+            switch (hints.urgency) {
+                case Urgency.LOW:
+                case Urgency.NORMAL:
+                    stockIcon = 'gtk-dialog-info';
+                    break;
+                case Urgency.CRITICAL:
+                    stockIcon = 'gtk-dialog-error';
+                    break;
+            }
+            return textureCache.load_icon_name(stockIcon, size);
+        }
+    },
+
     Notify: function(appName, replacesId, icon, summary, body,
                      actions, hints, timeout) {
         let source = this._sources[appName];
@@ -164,7 +195,7 @@ NotificationDaemon.prototype = {
         // been acknowledged.
         if (source == null) {
             let title = appNameMap[appName] || appName;
-            source = new Source(title, icon, hints);
+            source = new Source(title);
             Main.messageTray.add(source);
             this._sources[appName] = source;
 
@@ -184,8 +215,6 @@ NotificationDaemon.prototype = {
                 if (app)
                     source.setApp(app);
             });
-        } else {
-            source.update(icon, hints);
         }
 
         summary = GLib.markup_escape_text(summary, -1);
@@ -205,10 +234,12 @@ NotificationDaemon.prototype = {
             notification = this._currentNotifications[id];
         }
 
+        let iconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
         if (notification == null) {
             id = nextNotificationId++;
             notification = new MessageTray.Notification(source, summary, body,
-                                                        { bannerBody: true });
+                                                        { bannerBody: true,
+                                                          icon: iconActor });
             this._currentNotifications[id] = notification;
             notification.connect('dismissed', Lang.bind(this,
                 function(n) {
@@ -220,7 +251,8 @@ NotificationDaemon.prototype = {
                 }));
             notification.connect('action-invoked', Lang.bind(this, this._actionInvoked, source, id));
         } else {
-            notification.update(summary, body, { clear: true });
+            notification.update(summary, body, { icon: iconActor,
+                                                 clear: true });
         }
 
         if (actions.length) {
@@ -230,7 +262,8 @@ NotificationDaemon.prototype = {
 
         notification.setUrgent(hints.urgency == Urgency.CRITICAL);
 
-        source.notify(notification);
+        let sourceIconActor = this._iconForNotificationData(icon, hints, source.ICON_SIZE);
+        source.notify(sourceIconActor, notification);
         return id;
     },
 
@@ -300,57 +333,23 @@ NotificationDaemon.prototype = {
 
 DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
 
-function Source(title, icon, hints) {
-    this._init(title, icon, hints);
+function Source(title) {
+    this._init(title);
 }
 
 Source.prototype = {
     __proto__:  MessageTray.Source.prototype,
 
-    _init: function(title, icon, hints) {
+    _init: function(title) {
         MessageTray.Source.prototype._init.call(this, title);
 
         this.app = null;
         this._openAppRequested = false;
-
-        this.update(icon, hints);
-    },
-
-    update: function(icon, hints) {
-        this._icon = icon;
-        this._iconData = hints.icon_data;
-        this._urgency = hints.urgency;
     },
 
-    createIcon: function(size) {
-        let textureCache = St.TextureCache.get_default();
-
-        if (this._icon) {
-            if (this._icon.substr(0, 7) == 'file://')
-                return textureCache.load_uri_async(this._icon, size, size);
-            else if (this._icon[0] == '/') {
-                let uri = GLib.filename_to_uri(this._icon, null);
-                return textureCache.load_uri_async(uri, size, size);
-            } else
-                return textureCache.load_icon_name(this._icon, size);
-        } else if (this._iconData) {
-            let [width, height, rowStride, hasAlpha,
-                 bitsPerSample, nChannels, data] = this._iconData;
-            return textureCache.load_from_raw(data, data.length, hasAlpha,
-                                              width, height, rowStride, size);
-        } else {
-            let stockIcon;
-            switch (this._urgency) {
-                case Urgency.LOW:
-                case Urgency.NORMAL:
-                    stockIcon = 'gtk-dialog-info';
-                    break;
-                case Urgency.CRITICAL:
-                    stockIcon = 'gtk-dialog-error';
-                    break;
-            }
-            return textureCache.load_icon_name(stockIcon, size);
-        }
+    notify: function(icon, notification) {
+        this._setSummaryIcon(icon);
+        MessageTray.Source.prototype.notify.call(this, notification);
     },
 
     clicked: function() {
index 49504faada9847b53cc8e8d77500846008ec982e..c9d5b2448050c2e57043738694f317015082b412 100644 (file)
@@ -475,10 +475,13 @@ Source.prototype = {
         this._receivedId = this._channelText.connect('Received', Lang.bind(this, this._messageReceived));
 
         this._channelText.ListPendingMessagesRemote(false, Lang.bind(this, this._gotPendingMessages));
+
+        this._setSummaryIcon(this.createNotificationIcon());
     },
 
-    createIcon: function(size) {
-        return contactManager.createAvatar(this._conn, this._targetHandle, size);
+    createNotificationIcon: function() {
+        return contactManager.createAvatar(this._conn, this._targetHandle,
+                                           this.ICON_SIZE);
     },
 
     clicked: function() {
index 79a86dbb2421d2c1d72ef11e16b2d0eba677118a..85a16e770eae69bfb0fdcea010c50b38e07239a1 100644 (file)
@@ -94,10 +94,11 @@ Source.prototype = {
         MessageTray.Source.prototype._init.call(this, app.get_name());
         this._window = window;
         this._app = app;
+        this._setSummaryIcon(this.createNotificationIcon());
     },
 
-    createIcon : function(size) {
-        return this._app.create_icon_texture(size);
+    createNotificationIcon : function() {
+        return this._app.create_icon_texture(this.ICON_SIZE);
     },
 
     clicked : function() {